/**
 * \file    driver.cpp
 * \brief   Parser output.
 * \author  Jérôme LAURENS
 * \version 0.1
 * \date    September 23, 2014
 *
 * Reads a LALR(1) grammar, generates a parser based on a template file.
 * This is based on lemon parser generator, but has strongly been
 * revisited.
 *
 */

#include <memory>
#include <boost/format.hpp>
#include <boost/range/adaptor/reversed.hpp>
#include "generator.h"
#include "builder.h"
#include "driver.h"

LPP_NAMESPACE_BEGIN

#if 0
/* Print a #line directive line to the output file. */
PRIVATE void tplt_linedir(FILE *out, const int line_number_m, const String & filename)
{
    std::ostringstream S;
    S<<"#line "<<line_number_m<<" \""<<filename<<"\"\n";
    fprintf(out,"%s",S.str().c_str());
}

/* Print a string to the file and keep the linenumber up to date */
PRIVATE void tplt_print(FILE *out, struct LemonPP *lemp, const String & s, size_t *line_number_m)
{
    Stringstream ss(s);
    String line;
    if (!std::getline(ss, line)) {
        return;
    }
    String next_line;
    if (!std::getline(ss, next_line)) {
        // no line break
        fprintf(out,"%s",line.c_str());
        return;
    }
    while(true) {
        fprintf(out,"%s",line.c_str());
        ++(*line_number_m);
        line.swap(next_line);
        if (!std::getline(ss, next_line)) {
            // no more line break
            if (lemp->print_line_numbers) {
                ++(*line_number_m);
                tplt_linedir(out,*line_number_m,lemp->output_file_name);
            }
            fprintf(out,"%s",line.c_str());
            return;
        }
    }
}

/*
** Generate code which executes when the rule "rp" is reduced.  Write
** the code to "out".  Make sure line_number_m stays up-to-date.
*/
PRIVATE void Driver::emit_code(
        FILE *out,
        RuleRef rp,
        int *line_number_m
       ) {
    const char *cp;

    /* Generate code to do the reduce action */
    if (rp->code) {
        if (print_line_numbers) {
            ++(*line_number_m);
            tplt_linedir(out,rp->line,input_file_name);
        }
        fprintf(out,"{%s",rp->code);
        for(cp=rp->code; *cp; ++cp){
            if (*cp=='\n') ++(*line_number_m);
        } /* End loop */
        fprintf(out,"}\n"); ++(*line_number_m);
        if (print_line_numbers) { ++(*line_number_m); tplt_linedir(out,*line_number_m,output_file_name); }
    } /* End if (rp->code) */

    return;
}
#endif

/*
** Return the name of a C datatype able to represent values between
** lwr and upr, inclusive.
*/
String Driver::Minimum_size_type(long long int lwr, long long int upr) {
    if (lwr>=std::numeric_limits<int8_t>().min() && upr<=std::numeric_limits<int8_t>().max()) {
        return "int8_t";
    } else if (lwr>=std::numeric_limits<int16_t>().min() && upr<=std::numeric_limits<int16_t>().max()) {
        return "int16_t";
    } else if (lwr>=std::numeric_limits<int32_t>().min() && upr<=std::numeric_limits<int32_t>().max()) {
        return "int32_t";
    } else if (lwr>=std::numeric_limits<int64_t>().min() && upr<=std::numeric_limits<int64_t>().max()) {
        return "int64_t";
    } else{
        return "long long int";
    }
}
String Driver::Minimum_size_type(unsigned long long int upr) {
    if (upr <= std::numeric_limits<uint8_t>().max()) {
        return "uint8_t";
    } else if (std::numeric_limits<uint16_t>().max()) {
        return "uint16_t";
    } else if (std::numeric_limits<uint32_t>().max()) {
        return "uint32_t";
    } else if (std::numeric_limits<uint64_t>().max()) {
        return "uint64_t";
    } else {
        return "unsigned long long int";
    }
}

/*! Generate C source code for the parser */
/* Next are the tables used to determine what action to take based on the
** current state and lookahead token.  These tables are used to implement
** functions that take a state number and lookahead value and return an
** action integer.
**
** Suppose the action integer is N.  Then the action is determined as
** follows
**
**   0 <= N < YYNSTATE                  Shift N.  That is, push the lookahead
**                                      token onto the stack and goto state N.
**
**   YYNSTATE <= N < YYNSTATE+YYNRULE   Reduce by rule N-YYNSTATE.
**
**   N == YYNSTATE+YYNRULE              A syntax error has occurred.
**
**   N == YYNSTATE+YYNRULE+1            The parser accepts its input.
**
**   N == YYNSTATE+YYNRULE+2            No such action.  Denotes unused
**                                      slots in the yy_action[] table.
**
** The action table is constructed as a single large table named yy_action[].
** Given state S and lookahead X, the action is computed as
**
**      yy_action[ yy_shift_offset[S] + X ]
**
** If the index value yy_shift_offset[S]+X is out of range or if the value
** yy_lookahead[yy_shift_offset[S]+X] is not equal to X or if yy_shift_offset[S]
** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table
** and that yy_default(S) should be used instead.
**
** The formula above is for computing the action when the lookahead is
** a terminal symbol.  If the lookahead is a non-terminal (as occurs after
** a reduce action) then the yy_reduce_offset() array is used in place of
** the yy_shift_offset[] array and YY_REDUCE_USE_DFLT is used in place of
** YY_SHIFT_USE_DFLT.
**
** The following are the tables generated in this section:
**
**  yy_action[]        A single table containing all actions.
**  yy_lookahead[]     A table containing the lookahead for each entry in
**                     yy_action.  Used to detect hash collisions.
**  yy_shift_offset[]  For each state, the offset into yy_action for
**                     shifting terminals.
**  yy_reduce_offset() For each state, the offset into yy_action for
**                     shifting non-terminals after a reduce.
**  yy_default()       Default action for each state.
*/
#if defined(LPP_DO)
#   error Change that name
#endif
#define LPP_DO(WHAT)\
    string_list.push_back(WHAT)
#if defined(LPP_DO____)
#   error Change that name
#endif
#define LPP_DO____(WHAT)\
    do {String s(WHAT "\n"); string_list.push_back(s);} while(false)
#if defined(LPP_DO_FMT)
#   error Change that name
#endif
#define LPP_DO_FMT(HOW,WHAT)\
    string_list.push_back((Format(HOW "\n")%WHAT).str())

String const Driver::test_rule_names() const {
    std::ostringstream oss;
    RuleIndex i = 0;
    for (RuleRef const & rule: grammar()->ordered_rules()) {
        Format fmt("%1%=>%2% / ");
        fmt % i % rule->short_description();
        oss<<fmt.str();
        ++i;
    }
    return oss.str();
}
StringList const Driver::prepared_rule_names() const {
    StringList string_list;
    std::ostringstream oss;
    string_list.push_back("// Rule names\n");
    LPP_DO____("    struct RuleNames {");
    LPP_DO_FMT("        typedef %1% Value;","String");
    LPP_DO_FMT("        enum {k_index_max=%1%};",grammar()->number_of_rules());
    LPP_DO____("        Value operator[](Index const index) {");
    LPP_DO____("            LEMONPP_PARAMETER_ASSERT(index<k_index_max);");
    LPP_DO____("            static Value const value[] = {");
    RuleIndex i = 0;
    for (RuleRef const & rule: grammar()->ordered_rules()) {
        Format fmt(" /* %1% */ %2% \n");
        fmt % i % rule->short_description();
        string_list.push_back(fmt.str());
        ++i;
    }
    LPP_DO____("            };");
    LPP_DO____("            return value[index];");
    LPP_DO____("        }");
    LPP_DO____("    };");
    return string_list;
}

String const Driver::test_prepared_actions() const {
    std::ostringstream oss;
    for (Transactions::value_type const & value: builder_m.action_table()) {
        //DEBUG_MESSAGE("value: %lu => (%lu,%lu)",value.first,value.second.lookahead(),value.second.action());
        ActionIndex const what = value.second.action();
        oss<<what<<"/";
    }
    return oss.str();
}

String const Driver::test_lookaheads() const {
    std::ostringstream oss;
    for (Transactions::value_type const & pair: builder_m.action_table()) {
        SymbolIndex const what = pair.second.lookahead();
        oss<<what<<"/";
    }
    return oss.str();
}

StringList const Driver::prepared_actions() const {
    StringList string_list;
    std::ostringstream oss;
    LPP_DO_FMT("    struct %1% {","Actions");
    LPP_DO_FMT("        typedef %1% Value;",Minimum_size_type(generator().error_action_code()+1));
    LPP_DO_FMT("        enum {k_index_max=%1%};",builder_m.action_table().size());
    LPP_DO____("        Value operator[](Index const index) {");
    LPP_DO____("            LEMONPP_PARAMETER_ASSERT(index<k_index_max);");
    LPP_DO____("            static Value const value[] = {");
    int j = 0;
    ActionIndex i = 0;
    bool should_start = true;
    bool should_end = false;
    for (Transactions::value_type const & pair: builder_m.action_table()) {
        ActionIndex what = pair.second.action();
        if (what > generator().error_action_code()) {
            what = generator().error_action_code();
        }
        if (should_start) {
            oss<<" /* "<<i<<" */ ";
            should_start = false;
        }
        ++i;
        oss<<what;
        if (should_end) {
            oss<<std::endl;
            LPP_DO(oss.str());
            oss.str("");
            j=0;
            should_start = true;
            should_end = false;
        } else if (++j==9){
            should_end = true;
        }
    }
    if (!should_start) {
        oss<<std::endl;
        LPP_DO(oss.str());
        oss.str("");
    }
    LPP_DO____("            };");
    LPP_DO____("            return value[index];");
    LPP_DO____("        }");
    LPP_DO____("    };");
    return string_list;
}

StringList const Driver::prepared_lookaheads() const {
    StringList string_list;
    std::ostringstream oss;
    LPP_DO_FMT("    struct %1% {","Lookaheads");
    LPP_DO_FMT("        typedef %1% Value;",Minimum_size_type(generator().error_action_code()+1));
    LPP_DO_FMT("        enum {k_index_max=%1%};",builder_m.action_table().size());
    LPP_DO____("        Value operator[](Index const index) {");
    LPP_DO____("            LEMONPP_PARAMETER_ASSERT(index<k_index_max);");
    LPP_DO____("            static Value const value[] = {");
    int j = 0;
    ActionIndex i = 0;
    bool should_start = true;
    bool should_end = false;
    for (Transactions::value_type const & pair: builder_m.action_table()) {
        ActionIndex what = pair.second.lookahead();
        if (what>number_of_lookahead_symbols()) {
            what = number_of_lookahead_symbols();
        }
        if (should_start) {
            oss<<" /* "<<i<<" */ ";
            should_start = false;
        }
        ++i;
        oss<<what<<",";
        if (should_end) {
            oss<<std::endl;
            LPP_DO(oss.str());
            oss.str("");
            j=0;
            should_start = true;
            should_end = false;
        } else if (++j==9){
            should_end = true;
        }
    }
    if (!should_start) {
        oss<<std::endl;
        LPP_DO(oss.str());
        oss.str("");
    }
    LPP_DO____("            };");
    LPP_DO____("            return value[index];");
    LPP_DO____("        }");
    LPP_DO____("    };");
    return string_list;
}

String const Driver::test_shift_offsets() const {
    std::ostringstream oss;
    oss<<builder_m.min_terminal_offset()-1<<",";
    StateRefList::const_iterator b = states().begin();
    StateRefList::const_iterator e = states().end();
    while (b!=e) {
        --e;
        if (!(*e)->has_no_terminal_offset()) {
            ++e;
            break;
        }
    }
    for (;b!=e;++b) {
        StateRef const & state = * b;
        ActionOffset what = state->has_no_terminal_offset()?
                    builder_m.min_terminal_offset() - 1
                  : state->terminal_offset();
        oss<<what<<",";
    }
    return oss.str();
}

StringList const Driver::prepared_shift_offsets() const {
    StringList string_list;
    std::ostringstream oss;
    LPP_DO_FMT("    struct %1% {","ShiftOffsets");
    LPP_DO_FMT("        typedef %1% Value;",(Minimum_size_type(
                   builder_m.min_terminal_offset() - 1
                 , builder_m.max_terminal_offset())));
    StateRefList::const_iterator b = states().begin();
    StateRefList::const_iterator e = states().end();
    while (b!=e) {
        --e;
        if (!(*e)->has_no_terminal_offset()) {
            ++e;
            break;
        }
    }
    LPP_DO_FMT("        enum {k_index_max=%1%};",std::distance(b,e));
    LPP_DO____("        Value operator[](Index const index) {");
    LPP_DO____("            LEMONPP_PARAMETER_ASSERT(index<k_index_max);");
    LPP_DO____("            static Value const value[] = {");
    int j = 0;
    ActionIndex i = 0;
    bool should_start = true;
    bool should_end = false;
    for (;b!=e;++b) {
        StateRef const & state = * b;
        ActionOffset what = state->has_no_terminal_offset()?
                    builder_m.min_terminal_offset() - 1
                  : state->terminal_offset();
        if (should_start) {
            oss<<" /* "<<i<<" */ ";
            should_start = false;
        }
        ++i;
        oss<<what<<",";
        if (should_end) {
            oss<<std::endl;
            LPP_DO(oss.str());
            oss.str("");
            j=0;
            should_start = true;
            should_end = false;
        } else if (++j==9){
            should_end = true;
        }
    }
    if (!should_start) {
        oss<<std::endl;
        LPP_DO(oss.str());
        oss.str("");
    }
    LPP_DO____("            };");
    LPP_DO____("            return value[index];");
    LPP_DO____("        }");
    LPP_DO____("    };");
    return string_list;
}

String const Driver::test_reduce_offsets() const {
    std::ostringstream oss;
    oss<<builder_m.min_terminal_offset()-1<<",";
    StateRefList::const_iterator b = states().begin();
    StateRefList::const_iterator e = states().end();
    while (b!=e) {
        --e;
        if (!(*e)->has_no_non_terminal_offset()) {
            ++e;
            break;
        }
    }
    for (;b!=e;++b) {
        StateRef const & state = * b;
        ActionOffset what = state->has_no_non_terminal_offset()?
                    builder_m.min_terminal_offset() - 1
                  : state->non_terminal_offset();
        oss<<what<<",";
    }
    return oss.str();
}

StringList const Driver::prepared_reduce_offsets() const {
    StringList string_list;
    std::ostringstream oss;
    LPP_DO_FMT("    struct %1% {","ReduceOffsets");
    LPP_DO_FMT("        typedef %1% Value;",(Minimum_size_type(
                   builder_m.min_terminal_offset() - 1
                 , builder_m.max_non_terminal_offset())));
    StateRefList::const_iterator b = states().begin();
    StateRefList::const_iterator e = states().end();
    while (b!=e) {
        --e;
        if (!(*e)->has_no_non_terminal_offset()) {
            ++e;
            break;
        }
    }
    LPP_DO_FMT("        enum {k_index_max=%1%};",std::distance(b,e));
    LPP_DO____("        Value operator[](Index const index) {");
    LPP_DO____("            LEMONPP_PARAMETER_ASSERT(index<k_index_max);");
    LPP_DO____("            static Value const value[] = {");
    int j = 0;
    ActionIndex i = 0;
    bool should_start = true;
    bool should_end = false;
    for (;b!=e;++b) {
        StateRef const & state = * b;
        ActionOffset what = state->has_no_non_terminal_offset()?
                    builder_m.min_terminal_offset() - 1
                  : state->non_terminal_offset();
        if (should_start) {
            oss<<" /* "<<i<<" */ ";
            should_start = false;
        }
        ++i;
        oss<<what<<",";
        if (should_end) {
            oss<<std::endl;
            LPP_DO(oss.str());
            oss.str("");
            j=0;
            should_start = true;
            should_end = false;
        } else if (++j==9){
            should_end = true;
        }
    }
    if (!should_start) {
        oss<<std::endl;
        LPP_DO(oss.str());
        oss.str("");
    }
    LPP_DO____("            };");
    LPP_DO____("            return value[index];");
    LPP_DO____("        }");
    LPP_DO____("    };");
    return string_list;
}

StringList const Driver::prepared_fallbacks() const {
    StringList string_list;
    std::ostringstream oss;
    LPP_DO_FMT("    struct %1% {","FallBacks");
    LPP_DO_FMT("        typedef %1% Value;",Minimum_size_type(grammar()->number_of_terminals() - 1));
    LPP_DO_FMT("        enum {k_index_max=%1%};",states().size());
    LPP_DO____("        Value operator[](Index const index) {");
    LPP_DO____("            LEMONPP_PARAMETER_ASSERT(index<k_index_max);");
    LPP_DO____("            static Value const value[] = {");
    SymbolIndex i = 0;
    SymbolRefList::size_type mx = grammar()->number_of_terminals() - 1;
    while (mx>0 && grammar()->terminals()[mx]->fallback()==0) { --mx; }
    for(i=0; i<=mx; ++i){
        SymbolRef const & symbol = grammar()->terminals()[mx];
        oss.str(String(12,' '));
        SymbolRef fallback = symbol->fallback();
        if (fallback) {
            oss<<fallback->index()<<", /* "<<symbol->name()<<" => "<<fallback->name()<<" */"<<std::endl;
        } else {
            oss<<0<<", /* "<<symbol->name()<<" => none */"<<std::endl;
        }
        string_list.push_back(oss.str());
    }
    LPP_DO____("            };");
    LPP_DO____("            return value[index];");
    LPP_DO____("        }");
    LPP_DO____("    };");
    return string_list;
}

String const Driver::test_symbol_names() const {
    std::ostringstream oss;
    for (SymbolRef const & symbol: grammar()->terminals()) {
        oss<<symbol->name()<<"/";
    }
#ifdef WITH_ERROR_SYMBOL
    oss<<grammar()->error_symbol()->name()<<"/";
#endif
    for (SymbolRef const & symbol: grammar()->non_terminals()) {
        oss<<symbol->name()<<"/";
    }
    return oss.str();
}

StringList const Driver::prepared_symbol_names() const {
    StringList string_list;
    std::ostringstream oss;
    LPP_DO_FMT("    struct %1% {","TerminalNames");
    LPP_DO_FMT("        typedef %1% Value;","String");
    LPP_DO_FMT("        enum {k_index_max=%1%};",grammar()->number_of_lookahead_symbols());
    LPP_DO____("        Value operator[](Index const index) {");
    LPP_DO____("            LEMONPP_PARAMETER_ASSERT(index<k_index_max);");
    LPP_DO____("            static Value const value[] = {");
    SymbolIndex i = 0;
    oss.str("\"");
    for (SymbolRef const & symbol: grammar()->terminals()) {
        oss<<symbol->name()<<"\", ";
        if ((i&3)==3) {
            oss<<std::endl;
            LPP_DO(oss.str());
            oss.str("\"");
        } else {
            oss.str("\", \"");
        }
    }
    if ((i&3)==3) {
        oss<<std::endl;
        LPP_DO(oss.str());
    }
    i = 0;
    oss.str("\"");
    for (SymbolRef const & symbol: grammar()->non_terminals()) {
        oss<<symbol->name()<<"\", ";
        if ((i&3)==3) {
            oss<<std::endl;
            LPP_DO(oss.str());
            oss.str("\"");
        } else {
            oss.str("\", \"");
        }
    }
    if ((i&3)==3) {
        oss<<std::endl;
        LPP_DO(oss.str());
    }
    LPP_DO____("            };");
    LPP_DO____("            return value[index];");
    LPP_DO____("        }");
    LPP_DO____("    };");
    return string_list;
}

String const Driver::test_rule_informations() const {
    std::ostringstream oss;
    for (RuleRef const & rule: grammar()->ordered_rules()) {
        Format fmt("{%1%,%2%},");
        fmt % rule->lhs_symbol()->index() % rule->rhs_symbols().size();
        oss<<fmt.str();
    }
    return oss.str();
}

StringList const Driver::prepared_rule_informations() const {
    StringList string_list;
    std::ostringstream oss;
    LPP_DO_FMT("    struct %1% {","RuleInformations");
    Format fmt("        struct Value {action_m:%1%; %1% ;};\n");
    fmt % Minimum_size_type(grammar()->number_of_lookahead_symbols());
    LPP_DO(fmt.str());
    LPP_DO_FMT("        enum {k_index_max=%1%};",grammar()->number_of_rules());
    LPP_DO____("        Value operator[](Index const index) {");
    LPP_DO____("            LEMONPP_PARAMETER_ASSERT(index<k_index_max);");
    LPP_DO____("            static Value const value[] = {");
    for (RuleRef const & rule: grammar()->rules()) {
        Format fmt("  { %1%, %2% },\n");
        fmt % rule->lhs_symbol()->index() % rule->rhs_symbols().size();
        LPP_DO(fmt.str());
    }
    LPP_DO____("            };");
    LPP_DO____("            return value[index];");
    LPP_DO____("        }");
    LPP_DO____("    };");
    return string_list;
}

StringList const Driver::prepared_codes() const {
    StringList string_list;
    std::ostringstream oss;
    RuleRefList default_rules;
    RuleRefList L(grammar()->rules().begin(),grammar()->rules().begin());
    while (L.size()>0) {
        RuleRef const rule = L.front();
        L.pop_front();
        if (rule->code_lines().size()==0) {
            default_rules.push_back(rule);
            continue;
        }
        Format fmt(String(12,' ')+"case %1%: /* %2% */\n");
        fmt % rule->index() % rule->short_description();
        LPP_DO(fmt.str());
        RuleRefList LL;
        LL.swap(L);
        for (RuleRef const & rule2: LL) {
            if (rule2->code_lines()==rule->code_lines()) {
                fmt = Format(String(12,' ')+"case %1%: /* %2% */\n");
                fmt % rule2->index() % rule2->short_description();
                LPP_DO(fmt.str());
            } else {
                L.push_back(rule2);
            }
        }
        string_list.insert(string_list.cend(), rule->code_lines().cbegin(), rule->code_lines().cend());
        LPP_DO(String(12,' ')+"break;\n");
    }
    /* First output rules other than the default: rule */
    /* Finally, output the default: rule.  We choose as the default: all
        ** empty actions. */
    LPP_DO(String(12,' ')+"default:\n");
    for (RuleRef const & rule: default_rules) {
        Format fmt(String(12,' ')+"/* (%1%) %2% */\n");
        fmt % rule->index() % rule->short_description();
        LPP_DO(fmt.str());
    }
    LPP_DO(String(12,' ')+"break;\n");
    return string_list;
}

StringList const Driver::prepared_enum() const {
    StringList string_list;
    std::ostringstream oss;
    LPP_DO____("    struct Symbol {");
    LPP_DO____("        enum {");
    String indentation = String(12,' ');
    for (SymbolRef const & symbol: grammar()->terminals()) {
        Format fmt(indentation+"%1% = %2%,\n");
        fmt % symbol->name() % symbol->index();
        LPP_DO(fmt.str());
    }
    Format fmt = Format(indentation+"None = %1%,\n");
    fmt % (grammar()->number_of_lookahead_symbols()+1);
    LPP_DO(fmt.str());
    fmt = Format(indentation+"NonTerminal = %1%,\n");
    fmt % (grammar()->number_of_lookahead_symbols()+1);
    LPP_DO(fmt.str());
    fmt = Format();
    LPP_DO(indentation+"EndOfInput = 0,\n");
    LPP_DO____("        };");
    LPP_DO____("    };");
    return string_list;
}

void Driver::prepare_output() {
#warning please build_action_table before
    StringList string_list;
    std::ostringstream oss;
    String define("#define ");

#if defined(LPP_DO_DEFINE)
#   error change that macro name
#endif
#define LPP_DO_DEFINE(WHO,WHAT) \
    oss.str("#define " #WHO " "); \
    oss<<WHAT<<std::endl; \
    string_list.push_back(oss.str());
#if defined(LPP_DO_DEFINE_N)
#   error change that macro name
#endif
#define LPP_DO_DEFINE_N(WHO,WHAT) \
    oss.str("#define " #WHO " ("); \
    oss<<WHAT<<")"<<std::endl; \
    string_list.push_back(oss.str());
    LPP_DO_DEFINE(LEMONPP_CODETYPE,(Minimum_size_type(0, number_of_lookahead_symbols()+1)));
    LPP_DO_DEFINE(LEMONPP_NOCODE,number_of_lookahead_symbols()+1);
    LPP_DO_DEFINE(LEMONPP_ACTIONTYPE,(Minimum_size_type(0, number_of_states()+number_of_rules()+5)));
    if (wildcard_symbol()) {
        LPP_DO_DEFINE(LEMONPP_WILDCARD,wildcard_symbol()->index());
    }
    LPP_DO_DEFINE(LEMONPP_N_STATE,number_of_states());
    LPP_DO_DEFINE(LEMONPP_N_RULE,number_of_rules());
    LPP_DO_DEFINE(LEMONPP_N_SYMBOL,number_of_lookahead_symbols());
    LPP_DO_DEFINE_N(LEMONPP_ActionTableBuilder_COUNT,builder_m.number_of_actions());
    LPP_DO_DEFINE_N(LEMONPP_SHIFT_USE_DFLT,builder_m.min_terminal_offset()-1);
    Index n = number_of_states();
    for (StateRef const & state : boost::adaptors::reverse(states())) {
        if (state->has_no_terminal_offset()) break;
        --n;
    }
    LPP_DO_DEFINE_N(LEMONPP_SHIFT_COUNT,n);
    LPP_DO_DEFINE_N(LEMONPP_SHIFT_MIN,builder_m.min_terminal_offset());
    LPP_DO_DEFINE_N(LEMONPP_SHIFT_MAX,builder_m.max_terminal_offset());
    LPP_DO_DEFINE(LEMONPP_SHIFT_TYPE,(Minimum_size_type(builder_m.min_terminal_offset()-1, builder_m.max_terminal_offset())));
    LPP_DO_DEFINE_N(LEMONPP_REDUCE_USE_DFLT,builder_m.min_terminal_offset()-1);
    n = number_of_states();
    for (StateRef const & state : boost::adaptors::reverse(states())) {
        if (state->has_no_non_terminal_offset()) break;
        --n;
    }
    LPP_DO_DEFINE_N(LEMONPP_REDUCE_COUNT,n);
    LPP_DO_DEFINE_N(LEMONPP_REDUCE_MIN,builder_m.min_terminal_offset());
    LPP_DO_DEFINE_N(LEMONPP_REDUCE_MAX,builder_m.max_non_terminal_offset());
    LPP_DO_DEFINE_N(LEMONPP_REDUCE_TYPE,(Minimum_size_type(builder_m.min_terminal_offset()-1, builder_m.max_non_terminal_offset())));
    output_m["%%defines%%"] = string_list;
    string_list = StringList();

#ifdef WITH_ERROR_SYMBOL
    if (grammar()->use_error_symbol()) {
        LPP_DO_DEFINE(LEMONPP_ERROR_SYMBOL,grammar()->error_symbol()->index());
    }
#endif
    if (grammar()->has_fallback()) {
        LPP_DO_DEFINE(LEMONPP_FALLBACK, 1);
    }
    output_m["%%non type defines%%"] = string_list;
    string_list = StringList();

    /* Generate the parser defines of types */
    LPP_DO_DEFINE(LEMONPP_SYMBOL_TYPE,(Minimum_size_type(0, number_of_lookahead_symbols()+1)));
    LPP_DO_DEFINE(LEMONPP_STATE_TYPE,(Minimum_size_type(0, number_of_states())));
    LPP_DO_DEFINE(LEMONPP_RULE_TYPE,number_of_rules());
    output_m["%%parser type defines%%"] = string_list;
    string_list = StringList();

    /* Generate the defines of types */
    LPP_DO_DEFINE(LEMONPP_ACTION_TYPE,(Minimum_size_type(0, number_of_states()+number_of_rules()+5)));
    LPP_DO_DEFINE(LEMONPP_SHIFT_TYPE,(Minimum_size_type(builder_m.min_terminal_offset()-1, builder_m.max_terminal_offset())));
    LPP_DO_DEFINE(LEMONPP_REDUCE_TYPE,(Minimum_size_type(builder_m.min_terminal_offset()-1, builder_m.max_non_terminal_offset())));
    output_m["%%type defines%%"] = string_list;
    string_list = StringList();

#if defined(LPP_DO_ENUM)
#   error change that macro name
#endif
#define LPP_DO_ENUM(OFF7,WHO,WHAT) \
    oss.str(String(OFF7,' ')); \
    oss<<#WHO<<" = "<<WHAT<<","<<std::endl; \
    string_list.push_back(oss.str());
    LPP_DO_ENUM(12,kStateCount,number_of_states());
    LPP_DO_ENUM(12,kRuleCount,number_of_rules());
    LPP_DO_ENUM(12,kSymbolCount,number_of_lookahead_symbols());
    LPP_DO_ENUM(12,kTokenCount,number_of_states()+number_of_rules());
    LPP_DO_ENUM(12,kDefaultState,0);//USED ?
    LPP_DO_ENUM(12,kDefaultCode,0);//USED ?
    output_m["%%parser const enum%%"] = string_list;

    string_list = StringList();
    /* Generate the const for an enum enclosure */
    /* Action: */
    LPP_DO_ENUM(12,kActionCount,builder_m.number_of_actions());
    LPP_DO_ENUM(12,kActionError,number_of_states()+number_of_rules());
    LPP_DO_ENUM(12,kActionAccept,number_of_states()+number_of_rules()+1);
    LPP_DO_ENUM(12,kActionNone,number_of_states()+number_of_rules()+2);
    LPP_DO_ENUM(12,kShiftUseDefault,builder_m.min_terminal_offset()-1);
    n = number_of_states();
    for (StateRef const & state : boost::adaptors::reverse(states())) {
        if (state->has_no_terminal_offset()) break;
        --n;
    }
    LPP_DO_ENUM(12,kShiftCount,n);
    LPP_DO_ENUM(12,kShiftMin,builder_m.min_terminal_offset());
    LPP_DO_ENUM(12,kShiftMax,builder_m.max_terminal_offset());
    /* Reduce: */
    LPP_DO_ENUM(12,kReduceUseDefault,builder_m.min_terminal_offset()-1);
    n = number_of_states();
    for (StateRef const & state : boost::adaptors::reverse(states())) {
        if (state->has_no_non_terminal_offset()) break;
        --n;
    }
    LPP_DO_ENUM(12,kReduceCount,n);
    LPP_DO_ENUM(12,kReduceMin,builder_m.min_terminal_offset());
    LPP_DO_ENUM(12,kReduceMax,builder_m.max_non_terminal_offset());
    if (wildcard_symbol()) {
        LPP_DO_ENUM(12,kWildcard,wildcard_symbol()->index());
    }
    if (grammar()->has_fallback()) {
        SymbolIndex n = grammar()->number_of_terminals();
        for (SymbolRef const & symbol: boost::adaptors::reverse(grammar()->terminals())) {
            if (symbol->fallback()) break;
            --n;
        }
        LPP_DO_ENUM(12,kUseFallback,n);
    }
    output_m["%%const enum%%"] = string_list;
    string_list = StringList();

    /* Generate the action table and its associates:
        **
        **  yy_action[]        A single table containing all actions.
        **  yy_lookahead[]     A table containing the lookahead for each entry in
        **                     yy_action.  Used to detect hash collisions.
        **  yy_shift_ofst[]    For each state, the offset into yy_action for
        **                     shifting terminals.
        **  yy_reduce_ofst[]   For each state, the offset into yy_action for
        **                     shifting non-terminals after a reduce.
        **  yy_default[]       Default action for each state.
        */

    /* Output the yy_action table: model    */
    output_m["%%action table%%"] = prepared_actions();
    output_m["%%lookahead table%%"] = prepared_lookaheads();
    output_m["%%shift offset table%%"] = prepared_shift_offsets();
    output_m["%%reduce offset table%%"] = prepared_reduce_offsets();
    if (grammar()->has_fallback()) {
        output_m["%%fallback tokens table%%"] = prepared_fallbacks();
    }
    output_m["%%token name table%%"] = prepared_symbol_names();
    output_m["%%rule name table%%"] = prepared_rule_names();
    output_m["%%rule information table%%"] = prepared_rule_informations();
    output_m["%%reduce action code%%"] = prepared_codes();
    output_m["%%token enum%%"] = prepared_enum();
    string_list = StringList();
    /* Generate a list of tokens */
    for (SymbolRef const & symbol: grammar()->terminals()) {
        Format fmt(String(12,' ')+"LEMON_TOKEN_DESCRIPTION(%1%, %2%)\n");
        fmt % symbol->name() % symbol->index();
        string_list.push_back(fmt.str());
    }
    output_m["%%token list%%"] = string_list;
    string_list = StringList();
    return;
}

LPP_NAMESPACE_END
